/**
 *
 */
package org.tio.http.apitool.utils;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author tanyaowu
 */
public class HttpClientUtils {
	//    private static Logger log = LoggerFactory.getLogger(HttpClientUtils.class);

	public static final String encoding = "utf-8";

	private static Logger log = LoggerFactory.getLogger(HttpClientUtils.class);

	private static AtomicLong requestSeq = new AtomicLong();

	/**
	 * 将set-cookie转换成cookie
	 *
	 * @param setCookieHeaders
	 * @return
	 */
	public static List<Header> convertSetCookieToCookie(Header[] setCookieHeaders) {
		List<Header> cookieHeaders = null;
		if (setCookieHeaders != null) {
			cookieHeaders = new ArrayList<Header>();
			if (setCookieHeaders != null && setCookieHeaders.length > 0) {
				for (Header _header : setCookieHeaders) {

					cookieHeaders.add(new BasicHeader("Cookie", _header.getValue()));
				}
			}
		}
		return cookieHeaders;
	}

	/**
	 * @param method
	 * @return
	 */
	public static HttpRequestBase createHttpRequestBaseByMethod(String method) {
		HttpRequestBase ret = null;
		if (HttpGet.METHOD_NAME.equalsIgnoreCase(method)) {
			ret = new HttpGet();
		} else if (HttpPost.METHOD_NAME.equalsIgnoreCase(method)) {
			ret = new HttpPost();
		} else if (HttpDelete.METHOD_NAME.equalsIgnoreCase(method)) {
			ret = new HttpDelete();
		} else if (HttpHead.METHOD_NAME.equalsIgnoreCase(method)) {
			ret = new HttpHead();
		} else if (HttpOptions.METHOD_NAME.equalsIgnoreCase(method)) {
			ret = new HttpOptions();
		} else if (HttpTrace.METHOD_NAME.equalsIgnoreCase(method)) {
			ret = new HttpTrace();
		} else if (HttpPut.METHOD_NAME.equalsIgnoreCase(method)) {
			ret = new HttpPut();
		} else if (HttpPatch.METHOD_NAME.equalsIgnoreCase(method)) {
			ret = new HttpPatch();
		}
		return ret;
	}

	public static HttpResponse get(String url) throws ClientProtocolException, IOException {
		return get(url, (List<Header>) null, (HttpHost) null);
	}

	public static HttpResponse get(String url, List<Header> headers) throws ClientProtocolException, IOException {
		return get(url, headers, (HttpHost) null);
	}

	/**
	 * @param url
	 * @param headers
	 * @param proxy
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static HttpResponse get(String url, List<Header> headers, HttpHost proxy) throws ClientProtocolException, IOException {
		return httpRequest(url, null, headers, proxy, "get");
	}

	public static HttpResponse get(String url, List<Header> headers, HttpHost proxy, int timeout) throws ClientProtocolException, IOException {
		return httpRequest(url, null, headers, proxy, "get", timeout);
	}

	/**
	 * 
	 * @param url
	 * @param headers
	 * @param proxy
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static HttpResponse get(String url, Map<String, Object> headers, HttpHost proxy) throws ClientProtocolException, IOException {
		return get(url, headers, proxy, 100000);
	}

	public static HttpResponse get(String url, Map<String, Object> headers, HttpHost proxy, int timeout) throws ClientProtocolException, IOException {
		List<Header> requestHeaders = HttpClientUtils.mapToHeaders(headers);
		return get(url, requestHeaders, proxy, timeout);
	}

	public static List<Header> getCookieHeaders(HttpResponse response) throws Exception {
		Header[] setCookieHeaders = response.getHeaders("Set-Cookie");
		List<Header> _cookieHeaders = convertSetCookieToCookie(setCookieHeaders);
		return _cookieHeaders;
	}

	/**
	 * 获取cookie头
	 *
	 * @param requestParams
	 * @param url
	 * @param headers
	 * @param method
	 * @param proxy
	 * @return
	 * @throws Exception
	 */
	public static List<Header> getCookieHeaders(Map<String, Object> requestParams, String url, List<Header> headers, String method, HttpHost proxy) throws Exception {
		HttpResponse response = null;
		List<Header> cookieHeaders = new ArrayList<Header>();
		if (headers == null) {
			headers = new ArrayList<Header>();
		}

		while (true) {
			if ("get".equalsIgnoreCase(method)) {
				response = HttpClientUtils.get(url, headers, proxy);
			} else {
				response = HttpClientUtils.post(url, requestParams, headers, proxy);
			}

			StatusLine statusLine = response.getStatusLine();

			List<Header> headers1 = getCookieHeaders(response);
			if (headers1 != null) {
				headers.addAll(headers1);
				cookieHeaders.addAll(headers1);
			}

			if (statusLine.getStatusCode() == 302) //重定向
			{
				Header[] locationHeader = response.getHeaders("Location");
				url = locationHeader[0].getValue();
				continue;
			} else {
				break;
			}
		}
		return cookieHeaders;
	}

	public static void headersToMap(List<Header> headers, Map<String, Object> map) {
		if (headers != null) {
			for (Header header : headers) {
				map.put(header.getName(), header.getValue());
			}
		}
	}

	public static HttpResponse httpRequest(String uri, Map<String, Object> requestParams, List<Header> headers, HttpHost proxy, String method)
	        throws ClientProtocolException, IOException {
		return httpRequest(uri, requestParams, headers, proxy, method, null);
	}

	public static HttpResponse httpRequest(String uri, Map<String, Object> requestParams, List<Header> headers, HttpHost proxy, String method, int timeout)
	        throws ClientProtocolException, IOException {
		return httpRequest(uri, requestParams, headers, proxy, method, null, timeout);
	}

	public static HttpResponse httpRequest(String uri, Map<String, Object> requestParams, List<Header> headers, HttpHost proxy, String method, String requestBody)
	        throws ClientProtocolException, IOException {
		return httpRequest(uri, requestParams, headers, proxy, method, requestBody, 10000);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static HttpResponse httpRequest(String uri, Map<String, Object> requestParams, List<Header> headers, HttpHost proxy, String method, String requestBody, int timeout)
	        throws ClientProtocolException, IOException {
		long seq = requestSeq.incrementAndGet();

		CloseableHttpClient httpclient = HttpClients.createDefault();
//		HttpClientContext context = HttpClientContext.create();
		//		httpclient
		//				.getParams()
		//				.setParameter("User-Agent",
		//						"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11");
		HttpRequestBase httpRequestBase = createHttpRequestBaseByMethod(method);
		httpRequestBase.setURI(URI.create(uri));

		CloseableHttpResponse response = null;
		try {
			if (log.isInfoEnabled()) {
				StringBuilder sb4headers = new StringBuilder();
				if (headers != null) {
					sb4headers.append("\r\n[\r\n");
					for (Header header : headers) {
						sb4headers.append("{").append(header.getName()).append("  :  ");
						sb4headers.append(header.getValue()).append("}\r\n");
					}
					sb4headers.append("]\r\n");
				}

				StringBuilder sb4requestparams = new StringBuilder();
				if (requestParams != null) {
					sb4requestparams.append("\r\n[\r\n");
					Set<Map.Entry<String, Object>> entrySet = requestParams.entrySet();
					for (Map.Entry<String, Object> entry : entrySet) {
						sb4requestparams.append("{").append(entry.getKey()).append("  :  ");
						sb4requestparams.append(entry.getValue()).append("}\r\n");
					}
					sb4requestparams.append("]\r\n");
				}

				StringBuilder sb4cookies = new StringBuilder();

				log.info("request url {}:{}, \r\nheaders:{} \r\ncookies:{} \r\nrequestParams:{} \r\nrequestBody:{}", seq, uri, sb4headers, sb4cookies, sb4requestparams,
				        requestBody);
			}

			headers = perfectHeader(headers);
			if (headers != null) {

				headers.forEach(header -> {
					httpRequestBase.addHeader(header);
				});

				//                for (Header header : headers)
				//                {
				//                    httpRequestBase.addHeader(header);
				//                }
			}

			List<NameValuePair> nameValuePairs = null;// new ArrayList<NameValuePair>();

			if (requestParams != null) {
				nameValuePairs = new ArrayList<NameValuePair>();
				Set<Map.Entry<String, Object>> entrySet = requestParams.entrySet();

				for (Map.Entry<String, Object> entry : entrySet) {
					Object entryValue = entry.getValue();
					if (entryValue instanceof List) {
						List<String> list = (List) entryValue;
						for (String str : list) {
							nameValuePairs.add(new BasicNameValuePair(entry.getKey(), str));
						}
					} else {
						if (entryValue != null) {
							nameValuePairs.add(new BasicNameValuePair(entry.getKey(), "" + entryValue));
						}
					}

				}
			}
			if (nameValuePairs != null) {
				if (httpRequestBase instanceof HttpPost) {
					UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nameValuePairs, encoding);
					((HttpPost) httpRequestBase).setEntity(entity);
				}
			}

			if (requestBody != null) {
				if (httpRequestBase instanceof HttpPost) {
					StringEntity entity = new StringEntity(requestBody, encoding);
					//					 entity.setContentEncoding(encoding);
					//		             entity.setContentType("application/json");
					((HttpPost) httpRequestBase).setEntity(entity);
				} else {
					throw new RuntimeException("发送请求体时，必须用post!");
				}
			}
			RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).setProxy(proxy).build();

			httpRequestBase.setConfig(requestConfig);

			response = httpclient.execute(httpRequestBase);
			if (log.isInfoEnabled()) {
				log.info("response {}:{}", seq, response);
			}
			return response;

		} finally {
			//			if (response != null)
			//			{
			//				response.close();
			//			}
		}

	}

	/**
	 * @param requestHeaders
	 * @return
	 */
	public static List<Header> mapToHeaders(Map<String, Object> requestHeaders) {
		List<Header> headers = null;
		if (requestHeaders != null && requestHeaders.size() > 0) {
			headers = new ArrayList<Header>();
			Set<Entry<String, Object>> entrySet = requestHeaders.entrySet();

			for (Entry<String, Object> entry : entrySet) {
				headers.add(new BasicHeader(entry.getKey(), (String) entry.getValue()));
			}
		}
		return headers;
	}

	public static List<Header> perfectHeader(List<Header> headers) {
		if (headers == null) {
			headers = new ArrayList<Header>();
		}
		//		Header h = null;
		//		h = new BasicHeader("Accept-Encoding", "gzip, deflate");
		//		        headers.add(h);

		//		h = new BasicHeader("User-Agent",
		//				"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11");
		//		headers.add(h);
		//
		//        h = new BasicHeader("Accept-Language", "zh-CN");
		//        headers.add(h);

		//        h = new BasicHeader("Referer", "http://as.cnsuning.com/login.htm");   //告诉服务器我是从哪个页面链接过来的
		//        headers.add(h);

		//        h = new BasicHeader("Accept-Language", "zh-CN");
		//        headers.add(h);

		return headers;
	}

	public static HttpResponse post(String url, Map<String, Object> requestParams) throws ClientProtocolException, IOException {
		return post(url, requestParams, (Map<String, Object>) null, (HttpHost) null);
	}

	/**
	 * @param url
	 * @param requestParams
	 * @param headers
	 * @param proxy
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static HttpResponse post(String url, Map<String, Object> requestParams, List<Header> headers, HttpHost proxy) throws ClientProtocolException, IOException {
		return httpRequest(url, requestParams, headers, proxy, "post");
	}

	public static HttpResponse post(String url, Map<String, Object> requestParams, List<Header> headers, HttpHost proxy, int timeout) throws ClientProtocolException, IOException {
		return httpRequest(url, requestParams, headers, proxy, "post", timeout);
	}

	public static HttpResponse post(String url, Map<String, Object> requestParams, Map<String, Object> requestHeaders, HttpHost proxy) throws ClientProtocolException, IOException {
		return post(url, requestParams, requestHeaders, proxy, 10000);
	}

	public static HttpResponse post(String url, Map<String, Object> requestParams, Map<String, Object> requestHeaders, HttpHost proxy, int timeout)
	        throws ClientProtocolException, IOException {
		List<Header> headers = mapToHeaders(requestHeaders);
		return post(url, requestParams, headers, proxy, timeout);
	}

	public static HttpResponse post(String url, String requestBody, Map<String, Object> requestHeaders, HttpHost proxy) throws ClientProtocolException, IOException {
		return post(url, requestBody, requestHeaders, proxy, 10000);
	}

	public static HttpResponse post(String url, String requestBody, Map<String, Object> requestHeaders, HttpHost proxy, int timeout) throws ClientProtocolException, IOException {
		List<Header> headers = mapToHeaders(requestHeaders);
		return httpRequest(url, null, headers, proxy, "post", requestBody, timeout);
	}

	public static String toStr(HttpResponse httpResponse) throws Exception {
		String responseHtml = EntityUtils.toString(httpResponse.getEntity(), Charset.forName("utf-8"));
		return responseHtml;
	}

	/**
	 *
	 */
	public HttpClientUtils() {

	}
}